package com.yc.cinema.mybatis;

// reflect 反射
import org.apache.ibatis.annotations.Select;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class SqlSession {

    public <T> T getMapper(Class<T> clss){
        // 基于 JDK 动态代理
        // InvocationHandler 方法拦截器
        final Object o = Proxy.newProxyInstance(
                clss.getClassLoader(), // 类加载器
                new Class[]{clss},  // 类接口数组
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //System.out.println("proxy = " + proxy);
                        System.out.println("method = " + method);
                        System.out.println("args = " + Arrays.toString(args));

                        // 使用反射获取方法上的注解(select)
                        final Select select = method.getAnnotation(Select.class);
                        // 获取 sql语句
                        final String[] value = select.value();
                        String sql = value[0];
                        // 提取sql中的参数值
                        int index = 0;
                        // 参数集合
                        List<Object> params = new ArrayList<>();
                        while( (index = sql.indexOf("#{", index)) > -1){
                            // 查找右 }
                            int endIndex = sql.indexOf("}", index);
                            String name = sql.substring(index + 2, endIndex);
                            // 提取方法参数中的 指定的 name 值
                            if(method.getParameters().length == 1 ){
                                // 单个参数
                                final Parameter parameter = method.getParameters()[0];
                                // isPrimitive 基本数据类型 int, char ..
                                if(parameter.getType().isPrimitive()
                                        || parameter.getType() == String.class
                                        // || 包装器类型 Integer
                                 ){
                                    // 如果方法只有一个 简单基本数据类型(或 String)参数
                                    params.add(args[0]);
                                } else {
                                    // 否则就是对象参数  Hall, Movie
                                    // 通过反射获取属性值
                                    final Field field = args[0].getClass().getField(name);
                                    field.setAccessible(true);  // 设置强制访问, private
                                    final Object val = field.get(args[0]); // 获取字段值
                                    params.add(val);
                                }
                            } else {
                                // 多个参数 , 遍历每个参数的注解, 提取参数值
                                // 扩展
                            }
                            index = endIndex + 1;
                        }
                        System.out.println("params = " + params);
                        // 将sql 中的 #{...} 替换成 ?
                        sql = sql.replaceAll("#\\{\\w+\\}", "?");
                        System.out.println("sql = " + sql);

                        // DBHelper 执行 查询 , 语句sql, 参数数组: params.toArray()
                        // Map<String,Object> map = DBHelper.selectOneMap(sql, params.toArray())

                        // 通关反射获取返回值类型
                        final Class<?> returnType = method.getReturnType();
                        // 只实现 单个对象返回
                        Object ret = returnType.newInstance();  // Hall ret = new Hall();

                        Map<String,Object> map = null;

                        map.entrySet().forEach((entry)->{
                            try {
                                //entry.getKey(); // 字段名
                                //entry.getValue(); // 字段值
                                final Field field = ret.getClass().getField(entry.getKey());
                                field.setAccessible(true);  // 设置强制访问, private
                                field.set(ret,entry.getValue()); // 设置字段值
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        });

                        return ret;
                    }
                });
        return (T) o;
    }

}
